package com.zhenhe.core.common.utils.pay.bohai;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.PrivateKey;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.XMLUtils;
import org.apache.xpath.XPathAPI;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.zhenhe.core.common.global.Global;
import com.zhenhe.core.common.utils.pay.bohai.support.OfflineResolver;
import com.zhenhe.core.common.utils.pay.bohai.support.PacKeyManager;

public class BoHaiHelp implements InitializingBean{

	private static Logger log = BoHaiUtil.log;
	
	public static final String charset = "UTF-8";
	
	private static PacKeyManager pacKeyManager = new PacKeyManager();
	
	static{
		org.apache.xml.security.Init.init();
	}
	
	public String sign(String xmlContent, String transType) {
        String xmlSign = null;

        try {
            log.info("Begin sign :" + transType + ", xml:" + xmlContent);

            Document doc = string2Doc(xmlContent, charset);

            XMLSignature sign = new XMLSignature(doc, "",
                    XMLSignature.ALGO_ID_SIGNATURE_RSA);

            sign.getSignedInfo().addResourceResolver(new OfflineResolver());

            Node messageNode = doc.getElementsByTagName("Message").item(0);
            messageNode.appendChild(sign.getElement());

            Transforms transforms = new Transforms(doc);
            transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);

            sign.addDocument("#" + transType + "Req", transforms,
                    org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);

            PrivateKey key = pacKeyManager.getPrivateKey();
            
            // 签名
            sign.sign(key);

            xmlSign = doc2String(doc, charset);
        } catch (Exception e) {
            log.error("Error occur while sign !", e);
            e.printStackTrace();
        }

        return xmlSign;
    }

    public boolean verify(String xml) {
        boolean flag = false;

        try {
            Document doc = string2Doc(xml, charset);

            Element nscontext = XMLUtils.createDSctx(doc, "ds",
                    org.apache.xml.security.utils.Constants.SignatureSpecNS);

            Element signElement = (Element) XPathAPI.selectSingleNode(doc,
                    "//ds:Signature[1]", nscontext);

            if (null != signElement) {
                XMLSignature signature = new XMLSignature(signElement, "");
                log.info(signature.getBaseLocalName());
                log.info(signature.getBaseNamespace());
                log.info(signature.getBaseURI());

                flag = signature.checkSignatureValue(pacKeyManager
                        .getCertificate().getPublicKey());
            }else{
                log.info("null signElement");
            }
        } catch (Exception e) {
            log.error("Error occur while verify !", e);
        }

        return flag;
    }

    /**
     * @param xml
     * @return
     * @throws Exception
     */
    private Document string2Doc(String xml, String charset) throws Exception {

        InputStream is = IOUtils.toInputStream(xml, charset);

        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
                .newInstance();
        docBuilderFactory.setNamespaceAware(true);
        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

        Document doc = null;
        if (null != charset) {
            doc = docBuilder.parse(is, charset);
        } else {
            doc = docBuilder.parse(is);
        }
        return doc;
    }

    /**
     * @param doc
     * @return
     * @throws Exception
     */
    private String doc2String(Document doc, String charset) throws Exception {

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        if (null != charset) {
            t.setOutputProperty("encoding", charset);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        t.transform(new DOMSource(doc), new StreamResult(bos));
        String xmlStr = null;

        if (null != charset) {
            xmlStr = bos.toString(charset);
        } else {
            xmlStr = bos.toString();
        }
        return xmlStr;
    }

	@Override
	public void afterPropertiesSet() throws Exception {
		pacKeyManager = new PacKeyManager(Global.getConfig("bohai.key.path"));
		
	}
}
